/**
 *	Function Name
 * 		Returns: Its return the state or result of the function
 * 	@PARAMS: path
 *  @Auhtor: Amit Kumar
 *  @Date  :
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <string.h>
#include <jni.h>
#include "HTS_engine.h"

#include <android/log.h>

#define LOG_TAG   "libAMIT_HTS_ENGINE"
#define LOGI(ARG)  __android_log_write(ANDROID_LOG_INFO, LOG_TAG, ARG)
#define LOGE(ARG)  __android_log_write(ANDROID_LOG_ERROR,LOG_TAG, ARG)

#define INPUT_BUFF_SIZE 2048
/**
 * Global Variable
 *
 */

/* number of speakers for interpolation */

   double *rate_interp = NULL;  // Amit Note : Check For this variable

   /* file names of models */
   char **fn_ms_lf0;
   char **fn_ms_mcp;
   char **fn_ms_dur;
   char **fn_ms_str;
   /* number of each models for interpolation */
   int num_ms_lf0 = 0, num_ms_mcp = 0, num_ms_dur = 0, num_ms_str = 0;

   /* file names of trees */
   char **fn_ts_lf0;
   char **fn_ts_mcp;
   char **fn_ts_dur;
   char **fn_ts_str;
   /* number of each trees for interpolation */
   int num_ts_lf0 = 0, num_ts_mcp = 0, num_ts_dur = 0, num_ts_str = 0;

   /* file names of windows */
   char **fn_ws_lf0 = NULL;
   char **fn_ws_mcp;
   char **fn_ws_str;
   int num_ws_lf0 = 0, num_ws_mcp = 0, num_ws_str = 0;

   /* file names of global variance */
   char **fn_ms_gvl = NULL;
   char **fn_ms_gvm = NULL;
   char **fn_ms_gvs = NULL;
   int num_ms_gvl = 0, num_ms_gvm = 0, num_ms_gvs = 0;

   /* file names of global variance trees */
   char **fn_ts_gvl = NULL;
   char **fn_ts_gvm = NULL;
   char **fn_ts_gvs = NULL;
   int num_ts_gvl = 0, num_ts_gvm = 0, num_ts_gvs = 0;

   /* file name of global variance switch */
   char *fn_gv_switch = NULL;

   /* global parameter */
   int sampling_rate = 16000;
   int fperiod = 80;
   double alpha = 0.42;
   double stage = 0.0;          /* gamma = -1.0/stage */
   double beta = 0.0;
   int audio_buff_size = 1600;
   double uv_threshold = 0.5;
   double gv_weight_lf0 = 0.7;
   double gv_weight_mcp = 1.0;
   double gv_weight_str = 1.0;


   /* engine */ //
   HTS_Engine engine;

   /**
    * Some Comment Code which reference was not able to detected.
    * char *labfn = NULL;
    *
    */

/**
 * Init(char *path): This Function Initialise  The TTS Engine.
 * 		It take One Time Parameter.
 * 		Path of the Language Dir.
 *
 * 	Returns: Its return the state or result of the function
 * @PARAMS: path
 * @Auhtor: Amit Kumar
 * @Date  :
 *
 */

int Init(char *path)
{
	int i;
	int num_interp = 0;
	HTS_Boolean use_log_gain = FALSE;


/* initialize (stream[0] = spectrum , stream[1] = lf0) */
	HTS_Engine_initialize(&engine, 3);
/* prepare for interpolation */
	num_interp =  1; /*GetNumInterp(argc, argv);*/
	rate_interp = (double *) calloc(num_interp, sizeof(double));
	for (i = 0; i < num_interp; i++)
	  rate_interp[i] = 1.0;

	prepareLanguageParameter(path, num_interp); /*Function created By Amit Kumar*/

/* number of models,trees check */
	if (num_interp != num_ts_lf0 || num_interp != num_ts_mcp ||
	   num_interp != num_ts_dur || num_interp != num_ms_lf0 ||
	   num_interp != num_ms_mcp || num_interp != num_ms_dur) {
	  Error(1,
			"hts_engine: specify %d models(trees) for each parameter.\n",
			num_interp);
	}
	   /* load duration model */
	   HTS_Engine_load_duration_from_fn(&engine, fn_ms_dur, fn_ts_dur, num_interp);
	   /* load stream[0] (spectrum model) */
	   HTS_Engine_load_parameter_from_fn(&engine, fn_ms_mcp, fn_ts_mcp, fn_ws_mcp,
	                                     0, FALSE, num_ws_mcp, num_interp);
	   /* load stream[1] (lf0 model) */
	   HTS_Engine_load_parameter_from_fn(&engine, fn_ms_lf0, fn_ts_lf0, fn_ws_lf0,
	                                     1, TRUE, num_ws_lf0, num_interp);
	   HTS_Engine_load_parameter_from_fn(&engine, fn_ms_str, fn_ts_str, fn_ws_str,
	                                     2, FALSE, num_ws_str, num_interp);

	   /* load gv[0] (GV for spectrum) */
	   if (num_interp == num_ms_gvm) {
	      if (num_ms_gvm == num_ts_gvm)
	         HTS_Engine_load_gv_from_fn(&engine, fn_ms_gvm, fn_ts_gvm, 0,
	                                    num_interp);
	      else
	         HTS_Engine_load_gv_from_fn(&engine, fn_ms_gvm, NULL, 0, num_interp);
	   }
	   /* load gv[1] (GV for lf0) */
	   if (num_interp == num_ms_gvl) {
	      if (num_ms_gvl == num_ts_gvl)
	         HTS_Engine_load_gv_from_fn(&engine, fn_ms_gvl, fn_ts_gvl, 1,
	                                    num_interp);
	      else
	         HTS_Engine_load_gv_from_fn(&engine, fn_ms_gvl, NULL, 1, num_interp);
	   }
	   /* load gv[2] (GV for strengths) */
	   if (num_interp == num_ms_gvs) {
	      if (num_ms_gvs == num_ts_gvs)
	         HTS_Engine_load_gv_from_fn(&engine, fn_ms_gvs, fn_ts_gvs, 2,
	                                    num_interp);
	      else
	         HTS_Engine_load_gv_from_fn(&engine, fn_ms_gvs, NULL, 2, num_interp);
	   }
	   /* load GV switch */
	   if (fn_gv_switch != NULL)
		   HTS_Engine_load_gv_switch_from_fn(&engine, fn_gv_switch);


	   /* set parameter */
	   HTS_Engine_set_sampling_rate(&engine, sampling_rate);
	   HTS_Engine_set_fperiod(&engine, fperiod);
	   HTS_Engine_set_alpha(&engine, alpha);
	   HTS_Engine_set_gamma(&engine, stage);
	   HTS_Engine_set_log_gain(&engine, use_log_gain);
	   HTS_Engine_set_beta(&engine, beta);
	   HTS_Engine_set_audio_buff_size(&engine, audio_buff_size);
	   HTS_Engine_set_msd_threshold(&engine, 1, uv_threshold);      /* set voiced/unvoiced threshold for stream[1] */
	   HTS_Engine_set_gv_weight(&engine, 0, gv_weight_mcp);
	   HTS_Engine_set_gv_weight(&engine, 1, gv_weight_lf0);
	   HTS_Engine_set_gv_weight(&engine, 2, gv_weight_str);
	   for (i = 0; i < num_interp; i++) {
	      HTS_Engine_set_duration_interpolation_weight(&engine, i, rate_interp[i]);
	      HTS_Engine_set_parameter_interpolation_weight(&engine, 0, i,
	                                                    rate_interp[i]);
	      HTS_Engine_set_parameter_interpolation_weight(&engine, 1, i,
	                                                    rate_interp[i]);
		  HTS_Engine_set_parameter_interpolation_weight(&engine, 2, i,
	                                                    rate_interp[i]);
	   }
	   if (num_interp == num_ms_gvm)
	      for (i = 0; i < num_interp; i++)
	         HTS_Engine_set_gv_interpolation_weight(&engine, 0, i, rate_interp[i]);
	   if (num_interp == num_ms_gvl)
	      for (i = 0; i < num_interp; i++)
	         HTS_Engine_set_gv_interpolation_weight(&engine, 1, i, rate_interp[i]);
	   if (num_interp == num_ms_gvs)
	      for (i = 0; i < num_interp; i++)
	         HTS_Engine_set_gv_interpolation_weight(&engine, 2, i, rate_interp[i]);

	// end INIT


}
/**
 * Synth(char *text): This Function Synth
 * 		It take One Time Parameter.
 * 		Text to be converted into speech
 *
 * 	Returns: Its return the state or result of the function
 * @PARAMS: Text
 * @Auhtor: Amit Kumar
 * @Date  :
 *
 */

//int Synth(char *txt, double speech_speed)
int Synth(char *txt)
{

	HTS_Boolean phoneme_alignment = FALSE;
	double speech_speed = 1.0;
	double half_tone = 0.0;
	double f;
/* label list*/
	   char **label_data = NULL;
	   int label_size;



	if(txt==NULL)
		return -1;


	if (strlen(txt) > 0)
	   {
		   txt[strlen(txt)-1] = '\0';
		   label_data = Flite_HTS_Engine_L2S(txt, &label_size);
	   }

	   /* synthesis */
	   HTS_Engine_load_label_from_string_list(&engine, label_data, label_size);       /* load label file */

	   if (phoneme_alignment)       /* modify label */
	      HTS_Label_set_frame_specified_flag(&engine.label, TRUE);
	   if (speech_speed != 1.0)     /* modify label */
	      HTS_Label_set_speech_speed(&engine.label, speech_speed);
	   HTS_Engine_create_sstream(&engine);  /* parse label and determine state duration */
	   if (half_tone != 0.0) {      /* modify f0 */
	      for (i = 0; i < HTS_SStreamSet_get_total_state(&engine.sss); i++) {
	         f = HTS_SStreamSet_get_mean(&engine.sss, 1, i, 0);
	         f += half_tone * log(2.0) / 12;
	         if (f < log(10.0))
	            f = log(10.0);
	         HTS_SStreamSet_set_mean(&engine.sss, 1, i, 0, f);
	      }
	   }
	   HTS_Engine_create_pstream(&engine);  /* generate speech parameter vector sequence */
	   HTS_Engine_create_gstream(&engine);  /* synthesize speech */


}
/**
 *	generateWaveFile() :
 * 		Returns: Its return the state or result of the function
 * 	@PARAMS: path
 *  @Auhtor: Amit Kumar
 *  @Date  :
 *
 */



int generateWaveFile(char *wavFileName)
{
	FILE  *wavfp = NULL;
#ifndef HTS_EMBEDDED
		   FILE *lf0fp = NULL, *mcpfp = NULL;
#endif                          /* !HTS_EMBEDDED */

	// OUTPUT
	wavfp = Getfp(wavfile, "wb");
	   /* output */
	   if (wavfp)
	      HTS_Engine_save_riff(&engine, wavfp);
	#ifndef HTS_EMBEDDED
	   if (mcpfp)
	      HTS_Engine_save_generated_parameter(&engine, mcpfp, 0);
	   if (lf0fp)
	      HTS_Engine_save_generated_parameter(&engine, lf0fp, 1);
	#endif                          /* !HTS_EMBEDDED */
/* close files */
	#ifndef HTS_EMBEDDED
		if (mcpfp != NULL)
		  fclose(mcpfp);
		if (lf0fp != NULL)
		  fclose(lf0fp);
	#endif                          /* !HTS_EMBEDDED */
	if (wavfp != NULL)
	  fclose(wavfp);



}

/**
 *	destroy() : It will destroy the TTS_Engine
 * 		Returns: Its return the state or result of the function
 * 	@PARAMS: path
 *  @Auhtor: Amit Kumar
 *  @Date  :
 *
 */
int destroy()
{
	// Destory
	   /* free */
	   HTS_Engine_refresh(&engine);

	   /* free memory */
	   HTS_Engine_clear(&engine);
	   free(rate_interp);
	   free(fn_ws_mcp);
	   free(fn_ws_lf0);
	   free(fn_ms_mcp);
	   free(fn_ms_lf0);
	   free(fn_ms_dur);
	   free(fn_ts_mcp);
	   free(fn_ts_lf0);
	   free(fn_ts_dur);
	   free(fn_ms_gvm);
	   free(fn_ms_gvl);
	   free(fn_ts_gvm);
	   free(fn_ts_gvl);

	   return 0;

}


/**
 *	Getfg() : Its opens the file and return the FD.
 * 		Returns: Its return the File pointer.
 * 	@PARAMS: name, opt
 *  @Auhtor: Amit Kumar
 *  @Date  :
 *
 */

FILE *Getfp(const char *name, const char *opt)
{
   FILE *fp = fopen(name, opt);

   if (fp == NULL)
      Error(2, "Getfp: Cannot open %s.\n", name);

   return (fp);
}

/**
 *	addTwoStrinng : Its joins two strings
 * 		Returns: Its String
 * 	@PARAMS: String 1 String 2
 *  @Auhtor: Amit Kumar
 *  @Date  :
 *
 */

char* addTwoStrings(const char* str1, char* str2) {
    int m,n;
    int i, j;
    char *newString;
    m=strlen(str1);
    n=strlen(str2);
    newString = (char *) malloc(t); //*sizeof(char));
    for (i=0; i<m; i++)
            newString[i] = str1[i];

    for (j=0; j<n; j++)
            newString[i+j] = str2[j];
     newString[m+n] = '\0';
    return newString;

}

char *addTwoString(const char* str1, char* str2)
{
	char *newString;
	int m=strlen(str1);
	int n=strlen(str2);
	newString=(char *)malloc(m+n);
	strcpy(newString,str1);
	strcatnewString,str2);
	return newString;
}

int prepareLanguageParameter(char *dir, int num_interp)
{
	fn_ms_lf0 = (char **) calloc(num_interp, sizeof(char *));
	fn_ms_mcp = (char **) calloc(num_interp, sizeof(char *));
	fn_ms_dur = (char **) calloc(num_interp, sizeof(char *));
	fn_ms_str = (char **) calloc(num_interp, sizeof(char *));

	   fn_ts_lf0 = (char **) calloc(num_interp, sizeof(char *));
	   fn_ts_mcp = (char **) calloc(num_interp, sizeof(char *));
	   fn_ts_dur = (char **) calloc(num_interp, sizeof(char *));
	   fn_ts_str = (char **) calloc(num_interp, sizeof(char *));

	   fn_ms_gvl = (char **) calloc(num_interp, sizeof(char *));
	   fn_ms_gvm = (char **) calloc(num_interp, sizeof(char *));
	   fn_ms_gvs = (char **) calloc(num_interp, sizeof(char *));

	   fn_ts_gvl = (char **) calloc(num_interp, sizeof(char *));
	   fn_ts_gvm = (char **) calloc(num_interp, sizeof(char *));
	   fn_ts_gvs = (char **) calloc(num_interp, sizeof(char *));

	   /* delta window handler for log f0 */
	  fn_ws_lf0 = (char **) calloc(3, sizeof(char *));
	  /* delta window handler for mel-cepstrum */
	  fn_ws_mcp = (char **) calloc(3, sizeof(char *));
	  /* delta window handler for strengths */
	  fn_ws_str = (char **) calloc(3, sizeof(char *));

		   fn_ts_dur[num_ts_dur++] = addTwoStrings(dir, "/t.1");
		   fn_ts_lf0[num_ts_lf0++] = addTwoStrings(dir, "/p.1");
		   fn_ts_mcp[num_ts_mcp++] = addTwoStrings(dir, "/s.1");
		   fn_ts_str[num_ts_str++] = addTwoStrings(dir, "/b.1");

		   fn_ms_dur[num_ms_dur++] = addTwoStrings(dir, "/k.1");
		   fn_ms_lf0[num_ms_lf0++] = addTwoStrings(dir, "/v.1");
		   fn_ms_mcp[num_ms_mcp++] = addTwoStrings(dir, "/sy.1");
		   fn_ms_str[num_ms_str++] = addTwoStrings(dir, "/r.1");

		   fn_ws_lf0[num_ws_lf0++] = addTwoStrings(dir, "/l.w1");
		   fn_ws_lf0[num_ws_lf0++] = addTwoStrings(dir, "/l.w2");
		   fn_ws_lf0[num_ws_lf0++] = addTwoStrings(dir, "/l.w3");
		   fn_ws_mcp[num_ws_mcp++] = addTwoStrings(dir, "/m.w1");
		   fn_ws_mcp[num_ws_mcp++] = addTwoStrings(dir, "/m.w2");
		   fn_ws_mcp[num_ws_mcp++] = addTwoStrings(dir, "/m.w3");
		   fn_ws_str[num_ws_str++] = addTwoStrings(dir, "/s.w1");
		   fn_ws_str[num_ws_str++] = addTwoStrings(dir, "/s.w2");
		   fn_ws_str[num_ws_str++] = addTwoStrings(dir, "/s.w3");

}


/*****************************************************************************************/















/* Error: output error message */
void Error(const int error, char *message, ...)
{
	// Implements this function for Android.

	/* va_list arg;

   fflush(stdout);
   fflush(stderr);

   if (error > 0)
      fprintf(stderr, "\nError: ");
   else
      fprintf(stderr, "\nWarning: ");

   va_start(arg, message);
   vfprintf(stderr, message, arg);
   va_end(arg);

   fflush(stderr);
*/
   if (error > 0)
      exit(error);
}
// This Function will be implemented at later stage.

/* GetNumInterp: get number of speakers for interpolation from argv */
int GetNumInterp(int argc, char **argv_search)
{
   int num_interp = 1;
   while (--argc) {
      if (**++argv_search == '-') {
         if (*(*argv_search + 1) == 'i') {
            num_interp = atoi(*++argv_search);
            if (num_interp < 1) {
               num_interp = 1;
            }
            --argc;
         }
      }
   }
   return (num_interp);
}

char *addTwoStrings(const char *str1, char *str2);



int AMIT_TTS_ENGINE(char *dir, char *fname, char *wavfile)
{







   return 0;
}

/*NDK PART */

/**
 *  Code Snippet requried IN NDK
 *  const char *j_dir = (*env)->GetStringUTFChars(env, dir, 0);
 *  after work done
 *  (*env)->ReleaseStringUTFChars(env, dir, j_dir);
 *
 * to return jstring
 * 	return (*env)->NewStringUTF(env,"ERROR");
 *
 *
 *
 * */



/**********************************************************************************************************************************************/
/*	JNI-NDK Wrapper Function																												  */
/**********************************************************************************************************************************************/


JNIEXPORT jint JNICALL
Java_com_aksharspeech_ttsengine_TTSEngine_INIT(JNIEnv env, jobject thiz){
	//TODO : call native Init function.

	if(engine==NULL)
	{
		Init("/sdcard/rms")
	}
}


JNIEXPORT jint JNICALL
Java_com_aksharspeech_ttsengine_TTSEngine_TextToSpeech(JNIEnv env, jobject thiz, jstring textData, jstring wavFileName){
	//TODO : call native SYNTH function.
	/**
	 * Check if the engine started or not if not the start it ...
	 * */

	if(engine==NULL)
	{
		Init("/sdcard/rms");
	}
	if(engine!=NULL)
	{
		char *text;

		if(Synth(text))
		{
			char *wavfile;
			generateWaveFile(wavfile);

		}


	}

}

JNIEXPORT jint JNICALL
Java_com_aksharspeech_ttsengine_TTSEngine_Shutdown(JNIEnv env, jobject thiz){
	//TODO : call native destory function
	destroy();
	//TODO: need to memory handling and error handling at this stage or native stage.
}









